/******************************************************************************
* 文件名称： bsp_Key.c
* 模块名称： 通用按键模块
* 版 本 号： V1.0
* 作    者： PLAY
* 备    注： 通过状态机实现。按键扫描需定时执行，一般采用20ms。如果按键扫描放在中断中，
*            需配合FIFO使用。否则会丢失按键值导致按键不灵敏
* 修改记录： 版本     修改人      时间         修改内容
*			  V1.0	  PLAY	   2021.09.02		
*****************************************************************************/
 
/******************************************************************************
 头文件
*****************************************************************************/
#include "bsp_key.h"
#include "Config.h"
/******************************************************************************
 本文件宏定义 ('#define')                             
*****************************************************************************/
 
/*定义按键状态*/
#define KEY_STATE_INIT            	0
#define KEY_STATE_WOBBLE          	1
#define KEY_STATE_PRESS           	2
#define KEY_STATE_LONG            	3
#define KEY_STATE_CONTINUE        	4
#define KEY_STATE_RELEASE         	5
#define KEY_STATE_CONTINUE_RELEASE	6

/******************************************************************************
 本文件类型定义 ('typedef')                                         
*****************************************************************************/
 
#define uchar unsigned char
#define uint  unsigned short

/******************************************************************************
 本文件静态函数声明 ('static')
*****************************************************************************/
 
static uchar Key_GetIO(void);
static void Key_GpioInit(void);

/******************************************************************************
 全局变量定义 
*****************************************************************************/
 
uchar	s_u8KeyState = KEY_STATE_INIT ;
uint	s_u8KeyTimeCount = 0 ;
uint8_t gucKey_Stucked = 0;             /*按键粘滞*/
extern void USART2_Send_Frame(uint8_t *pData,uint16_t Len);

/*****************************************************************************
 函数实现 - global ('extern') and local ('static')
*****************************************************************************/
 
 
/******************************************************************************
* 功  能：按键初始化函数，主要完成按键FIFO的初始化
* 参  数：无
* 返回值：无
* 备  注：按键相关IO端口的初始，不在此函数中。需要在调用前初始化IO
*****************************************************************************/
void vBsp_Key_Init(void)
{
    muilt_cyc_fifo_error_code_t    lxState;
	MCF_ApplyFifo(KEY_FIFO_LENGTH,KEY_FIFO_ID,&lxState);     /*初始化按键队列*/       
	Key_GpioInit();						/*按键用到的IO端口初始化*/
}

#if (KEY_MODE != K5)
/******************************************************************************
* 功  能：读取按键相关的IO引脚状态，并根据IO状态返回对应键值
* 参  数：无
* 返回值：对应按键的键值
* 备  注：除K5按键外，其他按键通用
*****************************************************************************/
static uchar Key_GetIO(void) 
{    
	uchar keyvalue = 0;
    static uint8_t lucPowerKeyRelease = 0;/*标记Power键是否释放过*/
	
	/*读取6个IO口电平状态，没用到的端口，默认返回无效电平*/
	if(Read_key_1 == KEY_LEVEL)
	{
		keyvalue |= 0x01;
	}
	if(Read_key_2 == KEY_LEVEL)
	{
		keyvalue |= (0x01 << 1);
	}
	if(Read_key_3 == KEY_LEVEL)
	{
		keyvalue |= (0x01 << 2);
	}
	if(Read_key_4 == KEY_LEVEL)
	{
		keyvalue |= (0x01 << 3);
	}
    else
    {
        lucPowerKeyRelease = 1;/*标记Power键已经释放过*/
    }
	if(Read_key_5 == KEY_LEVEL)
	{
		keyvalue |= (0x01 << 4);
	}
    
    return keyvalue;
    
} 

#else

/******************************************************************************
* 功  能：按键延时用函数
* 参  数：无
* 返回值：K5按键专用，用于改变扫描线电平后的延时
*****************************************************************************/
static void Key_Delay(volatile uint32_t Delay)
{
	while(Delay > 0)
	{
		Delay--;
	}
}


/******************************************************************************
* 功  能：读取按键相关的IO引脚状态，并根据IO状态返回对应键值
* 参  数：无
* 返回值：K5按键专用
*****************************************************************************/
static uchar Key_GetIO(void) 
{   
	uint8_t keyvalue = 0;
	uint8_t Key_Up_Temp_0,Key_Up_Temp_1;
	uint8_t Key_Down_Temp_0,Key_Down_Temp_1;
	static uint8_t lucPowerKeyRelease = 0;/*标记Power键是否释放过*/

	/*读取扫描线拉高时Up线和Down线的状态（上次扫描结束时，扫描线就已经被拉高了*/
	Key_Up_Temp_1 	= Read_key_1;		
	Key_Down_Temp_1 = Read_key_3;
	
	Set_key_i_0;		/*再将扫描线拉低,读取此时Up线和Down线的电平状态*/
	Key_Delay(600);	/*电平变化后，加稍许延时，防止引脚电平变化不及时导致读取错误电平*/
	Key_Up_Temp_0	= Read_key_1;
	Key_Down_Temp_0 = Read_key_3;
	
	/*在任何状态下，UP线都为低电平，说明UP键被按下*/
	if(Key_Up_Temp_0 == KEY_LEVEL && Key_Up_Temp_1 == KEY_LEVEL)
	{
		keyvalue |= KEY_VALUE_1;
	}
	
	/*在任何状态下，Down线都为低电平，说明Down键被按下*/
	if(Key_Down_Temp_0 == KEY_LEVEL && Key_Down_Temp_1 == KEY_LEVEL)
	{
		keyvalue |= KEY_VALUE_3;
	}
	
	/*当扫描线为1，UP线为1，扫描线为0，UP线为0，认为i键按下*/
	if(Key_Up_Temp_1 == 1 && Key_Up_Temp_0 == 0)
	{
		keyvalue |= KEY_VALUE_2;
	}
	
	/*当扫描线为1，Down线为1，扫描线为0，Down线为0，认为大灯键按下*/
	if(Key_Down_Temp_1 == 1 && Key_Down_Temp_0 == 0)
	{
		keyvalue |= KEY_VALUE_5;
	}
	
	
	if(Read_key_4 == KEY_LEVEL)
	{
		keyvalue |= KEY_VALUE_4;
	}
    else
    {
        lucPowerKeyRelease = 1;/*标记Power键已经释放过*/
    }
	
	Set_key_i_1;		/*扫描线拉高，为下一次读取状态做准备*/
	
	/*在Power键未释放之前，不返回任何键值。防止Power键一直按着不松开后直接关机*/
    if(lucPowerKeyRelease)
    {
        return keyvalue;
    }
    else
    {
        return 0;
    }
} 
#endif

/******************************************************************************
* 功  能：按键扫描函数，该函数需定时调用，获取对应的按键状态并存入FIFO
* 参  数：无
* 返回值：无
*****************************************************************************/
uint8_t ucKey_Scan(void) 
{ 
	static uchar	s_u8LastKey = KEY_NULL ;  		/*保存最初按下时的键值*/ 
	
	static uint8_t WobbleCnt = 0;
	
    uchar KeyTemp = KEY_NULL ; 
	
    KeyTemp = Key_GetIO();        	/*获取键值*/ 

    switch(s_u8KeyState) 
    { 
        case KEY_STATE_INIT : 		/*初始状态*/
		{ 
			if(KEY_NULL != (KeyTemp)) 	/*键值不为0*/
			{ 
				s_u8LastKey = KeyTemp;				/*保存上次的键值*/
				KeyTemp = KEY_NULL;
				s_u8KeyState = KEY_STATE_WOBBLE ; 	/*进入消抖态*/
				WobbleCnt = 0;
			}
			s_u8KeyTimeCount = 0 ; 
			break ;
		} 
         
        case KEY_STATE_WOBBLE :      /*消抖*/ 
		{ 
			if(WobbleCnt < KEY_WOBBLE_PERIOD / KEY_SCAN_TIME)
			{
				WobbleCnt++;
			}
			else
			{
				s_u8KeyState = KEY_STATE_PRESS ; 
			}
			KeyTemp = KEY_NULL;
			break ;
		} 
         
        case KEY_STATE_PRESS : 
		{ 
			if((KEY_NULL != (KeyTemp)) && (s_u8LastKey == KeyTemp)) 
			{ 
				s_u8LastKey = KeyTemp ; 	/*保存键值,以便在释放按键状态返回键值*/ 
				//KeyTemp |= KEY_PRESS;  		/*注释掉这句，要求不响应按键按下*/ 
				KeyTemp = KEY_NULL;
				s_u8KeyState = KEY_STATE_LONG ; 
			} 
			else 
			{ 
				KeyTemp = KEY_NULL;
				s_u8KeyState = KEY_STATE_INIT ; 
			} 
			break ;
		} 
         
        case KEY_STATE_LONG : 
		{ 
			if(KEY_NULL != (KeyTemp)) 
			{
				if(KeyTemp == s_u8LastKey)
				{
					if(++s_u8KeyTimeCount > KEY_LONG_PERIOD / KEY_SCAN_TIME - 1) 
					{ 
						s_u8KeyTimeCount = 0 ; 
						KeyTemp |= KEY_LONG;  /*长按键事件发生*/ 
						
						s_u8KeyState = KEY_STATE_CONTINUE ; 
					} 
					else
					{
						KeyTemp = KEY_NULL;
					}
				}
				else
				{
					/*这一次的键值和上一次的键值不一致，说明有其它按键按下，返回初始状态*/
					KeyTemp = KEY_NULL;
					s_u8KeyState = KEY_STATE_INIT;
				}
			} 
			else 
			{ 
				KeyTemp = KEY_NULL;
				s_u8KeyState = KEY_STATE_RELEASE  ; 
			} 
			break ;
		} 
         
        case KEY_STATE_CONTINUE : 
		{ 
			if(KEY_NULL != (KeyTemp)) /*连发状态下，只有读取到键值为空，才返回初始状态*/
			{ 
				if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD / KEY_SCAN_TIME - 1) 
				{ 
					s_u8KeyTimeCount = 0 ; 
					//KeyTemp |= KEY_CONTINUE; 	/*注释掉这句，要求不响应连发状态*/
					KeyTemp = KEY_NULL;
				} 
				else
				{
					KeyTemp = KEY_NULL;
				}
			} 
			else 
			{   
				KeyTemp = KEY_NULL;
				//s_u8KeyState = KEY_STATE_INIT  ; 
				s_u8KeyState = KEY_STATE_CONTINUE_RELEASE; 
			} 
			break ;
		} 
        case KEY_STATE_CONTINUE_RELEASE:
		{
			KeyTemp = s_u8LastKey | KEY_CONTINUE_RELEASE;
			s_u8KeyState = KEY_STATE_INIT;
			break;
		}
        case KEY_STATE_RELEASE : 
		{ 
			s_u8LastKey |= KEY_RELEASE; 
			KeyTemp = s_u8LastKey ; 
			s_u8KeyState = KEY_STATE_INIT ; 
			break ;
		} 
		
        default : break ; 
    } 
	
	return KeyTemp;
}


/******************************************************************************
* 功  能：初始化按键用到的IO口
* 参  数：无
* 返回值：无
* 备  注：
*****************************************************************************/
static void Key_GpioInit(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  __HAL_RCC_GPIOB_CLK_ENABLE();
#if (KEY_MODE == K4)	
 
  /*Configure GPIO pins : PAPin PAPin */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 
#elif (KEY_MODE == K5)


#endif
}




